#ifndef _GUI_H_
#define _GUI_H_

#include <graphics.h>
#include <font.h>
#include <input.h>

////////////////////////////////////////////////////////////////////////////////

class Widget;

enum GUIEventType
{
  GUI_EVENT_MOUSE_PRESS,
  GUI_EVENT_MOUSE_RELEASE,
  GUI_EVENT_KEY_DOWN,
  GUI_EVENT_WIDGET
};

class GUIEvent
{
  public:
  virtual GUIEventType getType() const = 0;
};

class GUIMousePressEvent: public GUIEvent
{
  public:
  GUIMousePressEvent(GUIEventType type, const MousePressEvent& mouse_press):
    _type(type), _mouse_press(mouse_press) {}
  
  GUIEventType getType() const {return _type;}
  const MousePressEvent& getPress() const {return _mouse_press;}
  
  private:
  GUIEventType _type;
  MousePressEvent _mouse_press;
};

class GUIKeyDownEvent: public GUIEvent
{
  public:
  GUIKeyDownEvent(const KeyPressEvent& key_press): _key_press(key_press) {}
  
  GUIEventType getType() const {return GUI_EVENT_KEY_DOWN;}
  Key getKey() const {return _key_press.key;}
  char getChar() const {return _key_press.c;}
  
  private:
  KeyPressEvent _key_press;
};

class GUIWidgetEvent: public GUIEvent
{
  public:
  GUIWidgetEvent(Widget* widget, unsigned type): _widget(widget), _type(type) {}
  
  GUIEventType getType() const {return GUI_EVENT_WIDGET;}
  Widget* getWidget() const {return _widget;}
  unsigned getEventType() const {return _type;}
  
  private:
  Widget* _widget;
  unsigned _type;
};

class GUIEventListener
{
  public:
  virtual void handleEvent(const GUIEvent* event) = 0;
};

////////////////////////////////////////////////////////////////////////////////

class Widget
{
  public:
  Widget();
  virtual ~Widget() {}
  
  virtual const char* getTypeName() const = 0;
  
  const Point& getPos() const {return _pos;}
  void getGlobalPos(Point* pos) const;
   
  int width() const {return _dim.w;}
  int height() const {return _dim.h;}
  const Dimension& getDim() const {return _dim;}
  
  void setParent(Widget* parent) {_parent = parent;}
  void setPos(int x, int y) {_pos.set(x, y);}
  void setPos(const Point& pos) {_pos = pos;}
  void setDim(int w, int h) {_dim.set(w, h); onSetDim();}
  void setDim(const Dimension& dim) {_dim = dim; onSetDim();}
  
  bool intersect(const Point& pos);
  
  virtual Widget* getWidget(const Point& pos) {return this;}
  bool handleEvent(const GUIEvent* event);
  virtual bool handleMousePress(const MousePressEvent& event) {return false;}
  virtual bool handleMouseRelease(const MousePressEvent& event) {return false;}
  virtual bool handleKeyDown(const GUIKeyDownEvent* event) {return false;}
  
  virtual void loseFocus() {}
  virtual void gainFocus(){}
  
  virtual void doLayout() {}
  virtual void draw() = 0;
    
  protected:
  void postEvent(unsigned type);
  virtual void onSetDim() {}
  
  Widget* _parent;
  Point _pos;
  Dimension _dim;
};

////////////////////////////////////////////////////////////////////////////////

class Screen: public Widget
{
  public:
  Screen();
  
  const char* getTypeName() const {return "RootWidget";};
  
  void setBackground(Widget* widget);
  void setContent(Widget* widget);
  
  Widget* getWidget(const Point& pos);
  
  void doLayout();
  void draw();
  
  protected:
  void onSetDim();
  
  private:
  Widget* _background;
  Widget* _content;
};

////////////////////////////////////////////////////////////////////////////////

class State: public GUIEventListener
{
  public:
  State(): _done(false) {}
  virtual ~State() {}
  
  bool isDone() const {return _done;}
  void quit() {_done = true;}
  
  virtual bool create() = 0;
  virtual void destroy() = 0;
  
  virtual void update(unsigned dM, float dT) {}
  
  void handleEvent(const GUIEvent* event);
  virtual void handleKeyDown(const GUIKeyDownEvent* event) {}
  virtual void handleWidgetEvent(const GUIWidgetEvent* event) {}
  
  private:
  bool _done;
};

////////////////////////////////////////////////////////////////////////////////

bool GUI_init();
void GUI_quit();

void GUI_pushContext();
void GUI_popContext();

void GUI_loadDefaultFont(const char* filename, int size);
const Font& GUI_getDefaultFont();
void GUI_print(const Point& pos, Colour colour, const char* text);
void GUI_print(const Rect& rect, TextHorzAlign horz_align,
  TextVertAlign vert_align, Colour colour, const char* text);

void GUI_setBackground(Widget* widget);
void GUI_setContent(Widget* widget);

bool GUI_hasFocus(Widget* widget);

void GUI_postEvent(Widget* widget, const GUIEvent* event);
bool GUI_processEvents();
void GUI_update(unsigned dm, float dt);
void GUI_draw();

void GUI_runState(State* state);

#endif
